"""
Animation syntax parser for custom Markdown extensions.

Parses animation syntax like:
::fadeIn[duration=1.0,delay=0.5]
This is animated text
::

::slideIn[direction=left,duration=2.0]
- Animated bullet point
- Another point
::
"""

import re
from typing import Dict, List, Optional, Tuple, Any
from dataclasses import dataclass

from ...parser.content_types import AnimatedContent, AnimationConfig, ContentNode, ContentType
from ...utils.exceptions import MD2PPTError


@dataclass
class AnimationBlock:
    """Represents a parsed animation block."""
    animation_type: str
    content: str
    parameters: Dict[str, Any]
    start_line: int
    end_line: int


class AnimationParser:
    """Parses animation syntax from Markdown content."""
    
    # Animation block pattern: ::animationType[param=value,param2=value2]
    ANIMATION_START_PATTERN = r'^::([\w]+)(?:\[(.*?)\])?\s*$'
    ANIMATION_END_PATTERN = r'^::\s*$'
    
    # Parameter parsing pattern: param=value
    PARAM_PATTERN = r'(\w+)\s*=\s*([^,\]]+)'
    
    def __init__(self, config: Optional[AnimationConfig] = None):
        """Initialize the animation parser."""
        self.config = config or AnimationConfig()
        self._compiled_start = re.compile(self.ANIMATION_START_PATTERN, re.MULTILINE)
        self._compiled_end = re.compile(self.ANIMATION_END_PATTERN, re.MULTILINE)
        self._compiled_param = re.compile(self.PARAM_PATTERN)
    
    def parse_markdown_content(self, markdown_text: str) -> Tuple[str, List[AnimationBlock]]:
        """
        Parse markdown content and extract animation blocks.
        
        Returns:
            Tuple of (processed_markdown, animation_blocks)
        """
        lines = markdown_text.split('\n')
        animation_blocks = []
        processed_lines = []
        i = 0
        
        while i < len(lines):
            line = lines[i].strip()
            
            # Check for animation start
            start_match = self._compiled_start.match(line)
            if start_match:
                animation_type = start_match.group(1)
                params_str = start_match.group(2) or ""
                
                # Find the end of animation block
                content_lines = []
                start_line = i
                i += 1
                
                while i < len(lines):
                    if self._compiled_end.match(lines[i].strip()):
                        break
                    content_lines.append(lines[i])
                    i += 1
                
                if i >= len(lines):
                    raise MD2PPTError(f"Unclosed animation block starting at line {start_line + 1}")
                
                end_line = i
                content = '\n'.join(content_lines)
                parameters = self._parse_parameters(params_str)
                
                # Validate animation type
                if animation_type not in self.config.supported_animations:
                    raise MD2PPTError(f"Unsupported animation type: {animation_type}")
                
                animation_block = AnimationBlock(
                    animation_type=animation_type,
                    content=content,
                    parameters=parameters,
                    start_line=start_line,
                    end_line=end_line
                )
                animation_blocks.append(animation_block)
                
                # Replace animation block with placeholder or processed content
                processed_lines.append(f"<!-- ANIMATION_BLOCK_{len(animation_blocks) - 1} -->")
                processed_lines.append(content)
                processed_lines.append(f"<!-- /ANIMATION_BLOCK_{len(animation_blocks) - 1} -->")
            else:
                processed_lines.append(lines[i])
            
            i += 1
        
        processed_markdown = '\n'.join(processed_lines)
        return processed_markdown, animation_blocks
    
    def _parse_parameters(self, params_str: str) -> Dict[str, Any]:
        """Parse animation parameters from string."""
        parameters = {}
        
        if not params_str:
            return parameters
        
        matches = self._compiled_param.findall(params_str)
        for param_name, param_value in matches:
            # Try to convert to appropriate type
            param_value = param_value.strip().strip('"\'')
            
            # Convert numeric values
            if param_value.replace('.', '').isdigit():
                if '.' in param_value:
                    parameters[param_name] = float(param_value)
                else:
                    parameters[param_name] = int(param_value)
            # Convert boolean values
            elif param_value.lower() in ('true', 'false'):
                parameters[param_name] = param_value.lower() == 'true'
            else:
                parameters[param_name] = param_value
        
        return parameters
    
    def create_animated_content(self, content_node: ContentNode, 
                              animation_block: AnimationBlock) -> AnimatedContent:
        """Create AnimatedContent from ContentNode and AnimationBlock."""
        # Extract parameters with defaults
        duration = animation_block.parameters.get('duration', self.config.default_duration)
        delay = animation_block.parameters.get('delay', self.config.default_delay)
        easing = animation_block.parameters.get('easing', self.config.default_easing)
        direction = animation_block.parameters.get('direction')
        
        return AnimatedContent(
            content=content_node,
            animation_type=animation_block.animation_type,
            duration=float(duration),
            delay=float(delay),
            easing=str(easing),
            direction=direction
        )
    
    def has_animations(self, markdown_text: str) -> bool:
        """Check if markdown text contains animation syntax."""
        return bool(self._compiled_start.search(markdown_text))
    
    def get_supported_animations(self) -> List[str]:
        """Get list of supported animation types."""
        return self.config.supported_animations.copy()


class AnimationSyntaxError(MD2PPTError):
    """Raised when animation syntax is invalid."""
    pass